﻿using System;
using System.Data;
using System.IO;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using Petra.Gis.Geometry;

namespace Petra.Gis.DataServices.SHP
{
  /// <summary>
  /// This class represnts an ESRI Shapefile.
  /// </summary>
  public partial class ShpGeometryReader : IEnumerable
  {


    private string _filename;
    GeometryFactory gf = null;
    private GeometryBase _geometry;
    BinaryReader shpBinaryReader = null;


    /// <summary>
    /// Initializes a new instance of the Shapefile class with the given parameters.
    /// </summary>
    /// <param name="filename">The filename of the shape file to read (with .shp).</param>
    /// <param name="geometryFactory">The GeometryFactory to use when creating Geometry objects.</param>
    protected ShpGeometryReader(string filename)
    {
      if (filename == null)
        throw new ArgumentNullException("filename");

      _filename = filename;
      
      ResetAll();

    }




    public void ResetAll()
    {

      FileStream stream = new FileStream(_filename, System.IO.FileMode.Open, FileAccess.Read, FileShare.Read);
      shpBinaryReader = new BinaryReader(stream);
      _mainHeader = ShapefileHeader.Read(shpBinaryReader);

      // prepare geometry factory
      gf = new GeometryFactory();
      gf.CurrentGeomType = _mainHeader.ShapeType;

      // goto first record
      shpBinaryReader.BaseStream.Seek(100, SeekOrigin.Begin);

    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    protected bool MoveNext()
    {
      if (shpBinaryReader.PeekChar() != -1)
      {
        int recordNumber = EndianConverter.ToInt32(shpBinaryReader.ReadInt32(), DataEndian.BigIndian);
        int contentLength = EndianConverter.ToInt32(shpBinaryReader.ReadInt32(), DataEndian.BigIndian);
        contentLength *= 2;
        _geometry = gf.CreateGeometry(shpBinaryReader.ReadBytes(contentLength));
        //shpBinaryReader.BaseStream.Seek(contentLength - 4, SeekOrigin.Begin);
        return true;
      }
      else
      {
        // reached end of file, so close the reader.
        shpBinaryReader.Close();
        return false;
      }
    }





    private ShapefileHeader _mainHeader = null;


    /// <summary>
    /// Gets the bounds of the shape file.
    /// </summary>
    public ShapefileHeader Header
    {
      get
      {
        return _mainHeader;
      }
    }




    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetEnumerator()
    {
      while (MoveNext())
        yield return _geometry;
    }





    public static ShpGeometryReader Create(string filename)
    {

      if (filename == null)
        throw new ArgumentNullException("filename");

      // check for the file existing here, otherwise we will not get an error
      if (!File.Exists(filename))
        throw new FileNotFoundException(String.Format("Could not find file \"{0}\"", filename));

      return new ShpGeometryReader(filename);
      
    }


  }
}
